package com.example.interceptor;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@Intercepts({@Signature(type = StatementHandler.class,method ="prepare",args = {Connection.class,Integer.class}),
            @Signature(type = Executor.class,method = "query",args ={MappedStatement.class,
                    Object.class, RowBounds.class, ResultHandler.class})})
public class PageInteceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        if (invocation.getTarget() instanceof StatementHandler )
        {
            System.out.println("statementhandler inteceptor");
            StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
            MetaObject metaobject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
                    new DefaultReflectorFactory());
            //此处获取对象可以获取map或者用根据class获取对象，这边好好考虑
            Map map = (Map) statementHandler.getBoundSql().getParameterObject();//如果方法中you传单个对象，这边就报错,可以用instanceof判断
            //Page myPage = (Page)statementHandler.getBoundSql().getParameterObject();
            Page myPage = (Page) map.get("page");


            /*
            connection 项目组中和resultset那些都及时关闭了，spring-mybatis中官方用法自己关闭的
             */
            Connection connection = (Connection)invocation.getArgs()[0];
            ParameterHandler parameterHandler = (ParameterHandler) metaobject.getValue("delegate.parameterHandler");

            String sql = statementHandler.getBoundSql().getSql();
            System.out.println("statemenhandler sql"+sql);
            String countSql = "select count(*) from (" +sql+ ") a";
            PreparedStatement countStatement = connection.prepareStatement(countSql);
            parameterHandler.setParameters(countStatement);
            ResultSet resultCount = countStatement.executeQuery();

            if (resultCount.next())
                myPage.setTotalNumber(resultCount.getInt(1));

            myPage.count();
            String pageSql = sql + " limit " + myPage.getStartIndex()+ "," + myPage.getPageSize();
            metaobject.setValue("delegate.boundSql.sql",pageSql);

            return invocation.proceed();
        }
        else
        {
            System.out.println("exceutro page");
            final Object[] queryArgs = invocation.getArgs();
            final MappedStatement mappedStatement = (MappedStatement) queryArgs[0];
            final BoundSql boundSql = mappedStatement.getBoundSql(queryArgs[1]);

            String sql = boundSql.getSql();//项目中还判断是否；结尾，去除；
            Map myMap = (Map) queryArgs[1];
            Page myPage = (Page) myMap.get("page");//这边是个重点，根据传过来参数个数不同，object也是不同的

            //查询总共多少条记录符合要求
            int total = InterceptorUtil.quertTotal(mappedStatement,boundSql);
            myPage.setTotalNumber(total);
            myPage.count();
            String pageSql = sql + " limit " + myPage.getStartIndex()+ "," + myPage.getPageSize();

            /*
            copyFromNewSql 项目组中将替换后的sql塞到mappedstatement中的方法，、
            感觉不好，不如mataobject，但是mappestatement和boundsql都是statementhandler里面的属性哈
            能不能对mappedstatement获取metaobject对象，回去看看书
             */
            queryArgs[2] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
            queryArgs[0] = InterceptorUtil.copyFromNewSql(mappedStatement, boundSql, sql);//pageSql
            //此处应该是pagesql，因为我上面拦截了，所以这边写成老的sql，虽然拦截了两个，但是不会limit两次

            //这边就是将权限让给下一个拦截器，我这样到底算是几个拦截器？一个还是两个，应该是两个
            Object content = invocation.proceed();
            //这边等statementhandler拦截器都执行完才执行
            CarsPage<Object> carsPage = new CarsPage<>(myPage);
            carsPage.setCarEntities((List<Object>) content);

            // MyBatis 需要返回一个List对象，这里只是满足MyBatis而作的临时包装
            //如果我直接返回的话可能不行，因为返回一个carpage对象，而query方法是返回List，可能有问题
            List<Object> result = new ArrayList<>(1);
            result.add(carsPage);
            return result;
        }
    }



    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o,this);
    }

    @Override
    public void setProperties(Properties properties) {

    }


}
